/**
 * Copyright 2013 ABSir's Studio
 * 
 * All right reserved
 *
 * Create on 2013-6-13 下午2:27:29
 */
package com.absir.bean.core;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;

import com.absir.bean.basis.BeanDefine;
import com.absir.bean.basis.BeanFactory;
import com.absir.bean.basis.BeanScope;
import com.absir.bean.config.IBeanDefineAware;
import com.absir.bean.config.IBeanObjectProcessor;
import com.absir.bean.config.IBeanSoftReferenceAware;
import com.absir.core.dyna.DynaBinder;
import com.absir.core.kernel.KernelClass;
import com.absir.core.kernel.KernelCollection;
import com.absir.core.kernel.KernelLang.BreakException;
import com.absir.core.kernel.KernelLang.CloneTemplate;
import com.absir.core.kernel.KernelLang.FilterTemplate;
import com.absir.core.kernel.KernelString;

/**
 * @author absir
 * 
 */
public final class BeanFactoryImpl implements BeanFactory {

	/** Bean_Factory */
	private static BeanFactoryImpl Instance;

	/**
	 * @return
	 */
	protected static BeanFactoryImpl getInstance() {
		return Instance;
	}

	/** beanNameMapBeanDefine */
	private final Map<String, BeanDefine> beanNameDefineMap;

	/** beanDefineAwares */
	private final List<IBeanDefineAware> beanDefineAwares;

	/** beanObjectProcessors */
	private final List<IBeanObjectProcessor> beanObjectProcessors;

	/** beanSoftReferenceAwares */
	private final List<IBeanSoftReferenceAware> beanSoftReferenceAwares;

	/** beanSoftReferenceSet */
	private final Set<BeanSoftReference> beanSoftReferenceSet = new HashSet<BeanSoftReference>();

	/** beanNameDefineMapStack */
	private final Map<String, Stack<BeanDefine>> beanNameDefineMapStack = new HashMap<String, Stack<BeanDefine>>();

	/**
	 * @param beanNameDefineMap
	 * @param beanDefineAwares
	 * @param beanObjectProcessors
	 * @param beanSoftReferenceAwares
	 */
	protected BeanFactoryImpl(Map<String, BeanDefine> beanNameDefineMap, List<IBeanDefineAware> beanDefineAwares,
			List<IBeanObjectProcessor> beanObjectProcessors, List<IBeanSoftReferenceAware> beanSoftReferenceAwares) {
		Instance = this;
		this.beanNameDefineMap = beanNameDefineMap;
		this.beanDefineAwares = beanDefineAwares;
		this.beanObjectProcessors = beanObjectProcessors;
		this.beanSoftReferenceAwares = beanSoftReferenceAwares;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.absir.bean.basis.BeanFactory#getBeanObject(java.lang.String)
	 */
	@Override
	public Object getBeanObject(String beanName) {
		// TODO Auto-generated method stub
		BeanDefine beanDefine = beanNameDefineMap.get(beanName);
		if (beanDefine == null) {
			return null;
		}

		Object beanObject = beanDefine.getBeanObject();
		if (beanObject == null) {
			unRegisterBeanDefine(beanDefine);
		}

		return beanObject;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.absir.bean.basis.BeanFactory#getBeanObject(java.lang.Class)
	 */
	@Override
	public Object getBeanObject(Class<?> beanType) {
		// TODO Auto-generated method stub
		Iterator<Entry<String, BeanDefine>> iterator = beanNameDefineMap.entrySet().iterator();
		while (iterator.hasNext()) {
			BeanDefine beanDefine = iterator.next().getValue();
			if (beanType.isAssignableFrom(beanDefine.getBeanType())) {
				Object beanObject = beanDefine.getBeanObject();
				if (beanObject == null) {
					iterator.remove();

				} else {
					return beanObject;
				}
			}
		}

		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.absir.bean.basis.BeanFactory#getBeanObject(java.lang.String,
	 * java.lang.Class)
	 */
	@Override
	public Object getBeanObject(String beanName, Class<?> beanType) {
		// TODO Auto-generated method stub
		return getBeanObject(beanName, beanType, false);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.absir.bean.basis.BeanFactory#getBeanObject(java.lang.String,
	 * java.lang.Class, boolean)
	 */
	@Override
	public Object getBeanObject(String beanName, Class<?> beanType, boolean forcible) {
		// TODO Auto-generated method stub
		if (beanType.isArray() || Collection.class.isAssignableFrom(beanType)) {
			List<Object> beanObjects = getBeanObjects(KernelClass.componentClass(beanType));
			return DynaBinder.to(beanObjects, beanType);
		}

		if (KernelString.isEmpty(beanName)) {
			return getBeanObject(beanType);
		}

		Object beanObject = getBeanObject(beanName);
		if (beanObject != null) {
			if (beanType.isAssignableFrom(beanObject.getClass())) {
				return beanObject;

			} else {
				if (forcible) {
					throw new RuntimeException("beanName = " + beanName + " is " + beanObject + " not match "
							+ beanType);
				}

				beanObject = null;
			}
		}

		Iterator<Entry<String, BeanDefine>> iterator = beanNameDefineMap.entrySet().iterator();
		float max = -1.0f;
		while (iterator.hasNext()) {
			BeanDefine beanDefine = iterator.next().getValue();
			if (beanType.isAssignableFrom(beanDefine.getBeanType())) {
				Object object = beanDefine.getBeanObject();
				if (object == null) {
					iterator.remove();
					unRegisterBeanDefine(beanDefine);

				} else {
					if (beanName == null) {
						return object;

					} else {
						float similar = KernelString.similar(beanName, beanDefine.getBeanName());
						if (similar > max) {
							if (similar >= 1.0f) {
								return object;
							}

							max = similar;
							beanObject = object;
						}
					}
				}
			}
		}

		return beanObject;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.absir.bean.basis.BeanFactory#getBeanObject(java.lang.String,
	 * java.lang.reflect.Type, boolean)
	 */
	@Override
	public Object getBeanObject(String beanName, Type beanType, boolean forcible) {
		// TODO Auto-generated method stub
		Class<?> beanClass = KernelClass.rawClass(beanType);
		if (Collection.class.isAssignableFrom(beanClass)) {
			List<Object> beanObjects = getBeanObjects(KernelClass.componentClass(beanType));
			return DynaBinder.to(beanObjects, beanClass);

		} else {
			return getBeanObject(beanName, beanClass, forcible);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.absir.bean.basis.BeanFactory#getBeanObjects(java.lang.Class)
	 */
	@Override
	public List<Object> getBeanObjects(Class<?> beanType) {
		// TODO Auto-generated method stub
		List<Object> beanObjects = new ArrayList<Object>();
		Iterator<Entry<String, BeanDefine>> iterator = beanNameDefineMap.entrySet().iterator();
		while (iterator.hasNext()) {
			BeanDefine beanDefine = iterator.next().getValue();
			if (beanType.isAssignableFrom(beanDefine.getBeanType())) {
				Object bean = beanDefine.getBeanObject();
				if (bean == null) {
					iterator.remove();
					unRegisterBeanDefine(beanDefine);

				} else {
					beanObjects.add(bean);
				}
			}
		}

		return beanObjects;
	}

	/**
	 * 获取对象
	 * 
	 * @param filter
	 * @return
	 */
	public List<Object> getBeanObjects(FilterTemplate<BeanDefine> filter) {
		List<Object> beanObjects = new ArrayList<Object>();
		try {
			Iterator<Entry<String, BeanDefine>> iterator = beanNameDefineMap.entrySet().iterator();
			while (iterator.hasNext()) {
				BeanDefine beanDefine = iterator.next().getValue();
				if (filter.doWith(beanDefine)) {
					Object beanObject = beanDefine.getBeanObject();
					if (beanObject == null) {
						iterator.remove();
						unRegisterBeanDefine(beanDefine);

					} else {
						beanObjects.add(beanObject);
					}
				}
			}

		} catch (BreakException e) {
		}

		return beanObjects;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.absir.bean.basis.BeanFactory#getSoftReferenceBeans(java.lang.Class)
	 */
	@Override
	public List<Object> getSoftReferenceBeans(Class<?> beanType) {
		// TODO Auto-generated method stub
		List<Object> beans = new ArrayList<Object>();
		Iterator<BeanSoftReference> iterator = beanSoftReferenceSet.iterator();
		while (iterator.hasNext()) {
			BeanSoftReference beanSoftReference = iterator.next();
			Object beanObject = beanSoftReference.get();
			if (beanObject == null) {
				iterator.remove();
				unRegisterBeanSoftReference(beanSoftReference);
			}

			if (beanType.isAssignableFrom(beanObject.getClass())) {
				beans.add(beanObject);
			}
		}

		return beans;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.absir.bean.basis.BeanFactory#getSoftReferenceBeans(com.absir.core
	 * .kernel.KernelLang.FilterTemplate)
	 */
	@Override
	public List<Object> getSoftReferenceBeans(FilterTemplate<Object> filter) {
		// TODO Auto-generated method stub
		List<Object> beans = new ArrayList<Object>();
		try {
			Iterator<BeanSoftReference> iterator = beanSoftReferenceSet.iterator();
			while (iterator.hasNext()) {
				BeanSoftReference beanSoftReference = iterator.next();
				Object beanObject = beanSoftReference.get();
				if (beanObject == null) {
					iterator.remove();
					unRegisterBeanSoftReference(beanSoftReference);
				}

				if (filter.doWith(beanObject)) {
					beans.add(beanObject);
				}
			}

		} catch (BreakException e) {
		}

		return beans;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.absir.bean.basis.BeanFactory#processBeanObject(java.lang.Object)
	 */
	@Override
	public void processBeanObject(Object beanObject) {
		// TODO Auto-generated method stub
		processBeanObject(BeanScope.PROTOTYPE, beanObject);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.absir.bean.basis.BeanFactory#processBeanObject(com.absir.bean.basis
	 * .BeanScope, java.lang.Object)
	 */
	@Override
	public void processBeanObject(BeanScope beanScope, Object beanObject) {
		// TODO Auto-generated method stub
		if (beanObject == null) {
			return;
		}

		for (IBeanObjectProcessor beanObjectProcessor : beanObjectProcessors) {
			beanObjectProcessor.processBeanObject(this, beanScope, beanObject);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.absir.bean.basis.BeanFactory#registerBeanObject(java.lang.Object)
	 */
	@Override
	public void registerBeanObject(Object beanObject) {
		// TODO Auto-generated method stub
		registerBeanObject(null, beanObject);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.absir.bean.basis.BeanFactory#registerBeanObject(java.lang.String,
	 * java.lang.Object)
	 */
	@Override
	public void registerBeanObject(String beanName, Object beanObject) {
		// TODO Auto-generated method stub
		registerBeanObject(beanName, BeanScope.SINGLETON, beanObject);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.absir.bean.basis.BeanFactory#registerBeanObject(java.lang.String,
	 * com.absir.bean.basis.BeanScope, java.lang.Object)
	 */
	@Override
	public void registerBeanObject(String beanName, BeanScope beanScope, Object beanObject) {
		// TODO Auto-generated method stub
		BeanDefine beanDefine = beanScope == BeanScope.SINGLETON ? new BeanDefineSingleton(beanName, beanObject)
				: beanScope == BeanScope.SOFTREFERENCE || !(beanObject instanceof CloneTemplate) ? new BeanDefineSoftReference(
						beanName, beanObject) : new BeanDefineCloneable(beanName, (CloneTemplate<?>) beanObject);
		if (beanNameDefineMap.containsKey(beanDefine.getBeanName())) {
			throw new RuntimeException("beanName =" + beanDefine.getBeanName() + " has registered!");
		}

		registerBeanDefine(null, beanDefine);
	}

	/**
	 * 注册对象定义
	 * 
	 * @param beanDefine
	 */
	public void registerBeanDefine(BeanDefine beanDefine) {
		registerBeanDefine(beanNameDefineMap.get(beanDefine.getBeanName()), beanDefine);
	}

	/**
	 * 注册对象定义
	 * 
	 * @param registeredBeanDefine
	 * @param beanDefine
	 */
	protected void registerBeanDefine(BeanDefine registeredBeanDefine, BeanDefine beanDefine) {
		if (registeredBeanDefine != null) {
			unRegisterBeanDefine(beanDefine, null, null);
		}

		beanNameDefineMap.put(beanDefine.getBeanName(), beanDefine);
		for (IBeanDefineAware beanDefineAware : beanDefineAwares) {
			beanDefineAware.registerBeanDefine(this, beanDefine);
		}
	}

	/**
	 * 替换对象定义
	 * 
	 * @param beanDefine
	 */
	public void replaceRegisteredBeanDefine(BeanDefine beanDefine) {
		BeanDefine registeredBeanDefine = beanNameDefineMap.get(beanDefine.getBeanName());
		if (registeredBeanDefine == null) {
			registerBeanDefine(registeredBeanDefine, beanDefine);

		} else {
			replaceRegisteredBeanDefine(registeredBeanDefine, beanDefine);
		}
	}

	/**
	 * 替换对象定义
	 * 
	 * @param registeredBeanDefine
	 * @param beanDefine
	 */
	protected void replaceRegisteredBeanDefine(BeanDefine registeredBeanDefine, BeanDefine beanDefine) {
		if (registeredBeanDefine.getBeanType() == beanDefine.getBeanType()
				&& registeredBeanDefine.getBeanComponent() == beanDefine.getBeanComponent()) {
			beanNameDefineMap.put(beanDefine.getBeanName(), beanDefine);
			if (registeredBeanDefine.getBeanObject() != beanDefine.getBeanObject()) {
				for (IBeanDefineAware beanDefineAware : beanDefineAwares) {
					beanDefineAware.replaceRegisterBeanDefine(this, beanDefine);
				}
			}
		}
	}

	/**
	 * 注册栈对象
	 * 
	 * @param beanObject
	 */
	public void registerStackBeanObject(Object beanObject) {
		registerStackBeanDefine(new BeanDefineSingleton(beanObject));
	}

	/**
	 * 注册栈对象定义
	 * 
	 * @param beanDefine
	 */
	public void registerStackBeanDefine(BeanDefine beanDefine) {
		BeanDefine registeredBeanDefine = beanNameDefineMap.get(beanDefine.getBeanName());
		if (registeredBeanDefine == null) {
			registerBeanDefine(registeredBeanDefine, beanDefine);

		} else {
			Stack<BeanDefine> beanDefineStatck = beanNameDefineMapStack.get(beanDefine.getBeanName());
			if (beanDefineStatck == null) {
				beanDefineStatck = new Stack<BeanDefine>();
				beanNameDefineMapStack.put(beanDefine.getBeanName(), beanDefineStatck);
			}

			beanDefineStatck.push(registeredBeanDefine);
			replaceRegisteredBeanDefine(registeredBeanDefine, beanDefine);
		}
	}

	/**
	 * 注销栈对象
	 * 
	 * @param beanObject
	 */
	public void unRegisterStackBeanObject(Object beanObject) {
		unRegisterStackBeanDefine(new BeanDefineSingleton(beanObject));
	}

	/**
	 * 注销栈对象定义
	 * 
	 * @param beanDefine
	 */
	public void unRegisterStackBeanDefine(BeanDefine beanDefine) {
		Stack<BeanDefine> beanDefineStatck = beanNameDefineMapStack.get(beanDefine.getBeanName());
		if (beanDefineStatck == null || beanDefineStatck.size() <= 0) {
			unRegisterBeanDefine(beanDefine);

		} else {
			BeanDefine registeredBeanDefine = beanDefineStatck.pop();
			replaceRegisteredBeanDefine(registeredBeanDefine, registeredBeanDefine);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.absir.bean.basis.BeanFactory#unRegisterBeanObject(java.lang.Object)
	 */
	@Override
	public void unRegisterBeanObject(Object beanObject) {
		// TODO Auto-generated method stub
		unRegisterBeanObjectAll(null, beanObject);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.absir.bean.basis.BeanFactory#unRegisterBeanObject(java.lang.String)
	 */
	@Override
	public void unRegisterBeanObject(String beanName) {
		// TODO Auto-generated method stub
		BeanDefine beanDefine = beanNameDefineMap.get(beanName);
		if (beanDefine != null) {
			unRegisterBeanDefine(beanDefine, null, null);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.absir.bean.basis.BeanFactory#unRegisterBeanObject(java.lang.String,
	 * java.lang.Object)
	 */
	@Override
	public void unRegisterBeanObject(String beanName, Object beanObject) {
		// TODO Auto-generated method stub
		beanName = BeanDefineType.getBeanName(beanName, beanObject.getClass());
		unRegisterBeanDefine(beanNameDefineMap.get(beanName), beanObject, null);
	}

	/**
	 * 注销对象定义
	 * 
	 * @param beanDefine
	 */
	public void unRegisterBeanDefine(BeanDefine beanDefine) {
		unRegisterBeanDefine(beanNameDefineMap.get(beanDefine.getBeanName()), null, beanDefine);
	}

	/**
	 * 注销对象定义
	 * 
	 * @param registeredBeanDefine
	 * @param object
	 * @param beanDefine
	 */
	protected void unRegisterBeanDefine(BeanDefine registeredBeanDefine, Object object, BeanDefine beanDefine) {
		if (registeredBeanDefine != null) {
			if (beanDefine != null && !registeredBeanDefine.equals(beanDefine)) {
				throw new RuntimeException("beanName + " + registeredBeanDefine.getBeanName() + " unRegister "
						+ beanDefine + " not match " + registeredBeanDefine);

			} else if (object != null) {
				if (!(registeredBeanDefine.getBeanScope() == BeanScope.PROTOTYPE ? registeredBeanDefine.getBeanType() == object
						.getClass() : object.equals(registeredBeanDefine.getBeanObject()))) {
					throw new RuntimeException("beanName + " + registeredBeanDefine.getBeanName() + " unRegister "
							+ beanDefine + " not match " + object);
				}
			}

			beanNameDefineMap.remove(registeredBeanDefine.getBeanName());
			for (IBeanDefineAware beanDefineAware : beanDefineAwares) {
				beanDefineAware.unRegisterBeanDefine(this, registeredBeanDefine);
			}
		}
	}

	/**
	 * 获取全部对象定义
	 * 
	 * @return
	 */
	public BeanDefine[] getBeanDefines() {
		return KernelCollection.toArray(beanNameDefineMap.values(), BeanDefine.class);
	}

	/**
	 * 获取对象定义
	 * 
	 * @param beanName
	 * @return
	 */
	public BeanDefine getBeanDefine(String beanName) {
		return beanNameDefineMap.get(beanName);
	}

	/**
	 * 获取唯一对象定义
	 * 
	 * @param beanComponent
	 * @return
	 */
	public BeanDefine getBeanDefineComponent(Object beanComponent) {
		for (BeanDefine beanDefine : beanNameDefineMap.values()) {
			Object component = beanDefine.getBeanComponent();
			if (component != null) {
				if (component.equals(beanComponent)) {
					return beanDefine;
				}
			}
		}

		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.absir.bean.basis.BeanFactory#registerBeanSoftObject(java.lang.Object)
	 */
	@Override
	public void registerBeanSoftObject(Object beanObject) {
		// TODO Auto-generated method stub
		if (!beanSoftReferenceSet.contains(beanObject)) {
			registerBeanSoftReference(new BeanSoftReference(beanObject));
		}
	}

	/**
	 * 注册引用对象
	 * 
	 * @param beanSoftReference
	 */
	public synchronized void registerBeanSoftReference(BeanSoftReference beanSoftReference) {
		if (beanSoftReferenceSet.add(beanSoftReference)) {
			for (IBeanSoftReferenceAware beanSoftReferenceAware : beanSoftReferenceAwares) {
				beanSoftReferenceAware.registerSoftReference(this, beanSoftReference);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.absir.bean.basis.BeanFactory#unRegisterBeanSoftObject(java.lang.Object
	 * )
	 */
	@Override
	public void unRegisterBeanSoftObject(Object beanObject) {
		// TODO Auto-generated method stub
		if (beanSoftReferenceSet.contains(beanObject)) {
			unRegisterBeanSoftReference(new BeanSoftReference(beanObject));
		}
	}

	/**
	 * 注销引用对象
	 * 
	 * @param beanObject
	 */
	public void unRegisterBeanSoftReference(BeanSoftReference beanSoftReference) {
		synchronized (beanSoftReferenceSet) {
			if (beanSoftReferenceSet.remove(beanSoftReference)) {
				for (IBeanSoftReferenceAware beanSoftReferenceAware : beanSoftReferenceAwares) {
					beanSoftReferenceAware.unRegisterSoftReference(this, beanSoftReference);
				}
			}
		}
	}

	/**
	 * 注销对象清理
	 * 
	 * @param beanName
	 * @param beanObject
	 */
	public void unRegisterBeanObjectAll(String beanName, Object beanObject) {
		unRegisterBeanObjectAll(beanName, beanObject);
		unRegisterBeanSoftObject(beanObject);
	}

	/**
	 * 清除对象软定义
	 */
	public void clearBeanDefine() {
		Iterator<Entry<String, BeanDefine>> iterator = beanNameDefineMap.entrySet().iterator();
		while (iterator.hasNext()) {
			BeanDefine beanDefine = iterator.next().getValue();
			if (beanDefine.getBeanObject() == null) {
				iterator.remove();
				unRegisterBeanDefine(beanDefine, null, null);
			}
		}
	}

	/**
	 * 清除对象软引用
	 */
	public void clearBeanSoftReference() {
		Iterator<BeanSoftReference> iterator = beanSoftReferenceSet.iterator();
		while (iterator.hasNext()) {
			BeanSoftReference beanSoftReference = iterator.next();
			if (beanSoftReference.get() == null) {
				iterator.remove();
				unRegisterBeanSoftReference(beanSoftReference);
			}
		}
	}

	/**
	 * 清除对象工厂软引用
	 */
	public void clearBeanFactory() {
		clearBeanDefine();
		clearBeanSoftReference();
	}
}
